<template>
  <div class="cosy-container">
    <el-card class="main-card">
      <div slot="header" class="card-header">
        <h2>CosyVoice 语音合成</h2>
      </div>

      <!-- 模式选择 -->
      <el-form :model="form" label-width="100px" class="cosy-form">
        <el-form-item label="合成模式">
          <el-select v-model="selectedMode" @change="handleModeChange" placeholder="请选择合成模式" style="width: 100%">
            <el-option
              v-for="mode in modes"
              :key="mode"
              :label="mode"
              :value="mode">
            </el-option>
          </el-select>
        </el-form-item>

        <!-- 文本输入 - 隐藏原来的输入框 -->
        <div v-if="false">
          <el-form-item label="输入文本">
            <el-input
              type="textarea"
              v-model="ttsText"
              :rows="4"
              placeholder="请输入要转换的文本">
            </el-input>
          </el-form-item>
        </div>

        <!-- SpringBoot交互部分 -->
        <el-form-item label="输入参数">
          <el-input
            v-model="springbootParam"
            placeholder="请输入要发送给后端的参数"
            class="param-input">
          </el-input>
          <el-button 
            type="success" 
            @click="fetchTextFromSpringBoot" 
            :loading="isLoadingText"
            style="margin-top: 10px; width: 100%">
            获取文本并生成语音
          </el-button>
        </el-form-item>

        <!-- 接收到的文本显示 -->
        <el-form-item v-if="receivedText" label="接收文本">
          <div class="received-text-box">
            <p>{{ receivedText }}</p>
          </div>
        </el-form-item>

        <!-- 预训练音色选择 -->
        <el-form-item v-if="selectedMode === '预训练音色'" label="选择音色">
          <el-select v-model="selectedSpeaker" placeholder="请选择音色" style="width: 100%">
            <el-option
              v-for="speaker in speakers"
              :key="speaker"
              :label="speaker"
              :value="speaker">
            </el-option>
          </el-select>
        </el-form-item>

        <!-- 自然语言控制 -->
        <el-form-item v-if="selectedMode === '自然语言控制'" label="指令文本">
          <el-input
            type="textarea"
            v-model="instructText"
            :rows="3"
            placeholder="请输入指令文本">
          </el-input>
        </el-form-item>

        <!-- 语音克隆 -->
        <template v-if="['3s极速复刻', '跨语种复刻'].includes(selectedMode)">
          <el-form-item label="示例音频">
            <el-upload
              class="upload-demo"
              drag
              action="#"
              :auto-upload="false"
              :on-change="handleAudioUpload"
              :limit="1"
              accept="audio/*">
              <i class="el-icon-upload"></i>
              <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
              <div class="el-upload__tip" slot="tip">支持 wav 格式音频文件</div>
            </el-upload>
          </el-form-item>

          <el-form-item v-if="selectedMode === '3s极速复刻'" label="示例文本">
            <el-input
              type="textarea"
              v-model="promptText"
              :rows="3"
              placeholder="请输入示例文本">
            </el-input>
          </el-form-item>
        </template>

        <!-- 语速调节 -->
        <el-form-item label="语速调节">
          <el-slider
            v-model="speed"
            :min="0.5"
            :max="2"
            :step="0.1"
            :format-tooltip="value => value + 'x'"
            style="width: 100%">
          </el-slider>
        </el-form-item>

        <!-- 分段处理选项 -->
        <el-form-item label="分段处理">
          <el-switch v-model="enableSegmentation"></el-switch>
          <span style="margin-left: 10px">长文本分段处理</span>
        </el-form-item>

        <!-- 生成按钮 - 隐藏原来的按钮 -->
        <div v-if="false">
          <el-form-item>
            <el-button 
              type="primary" 
              @click="generateSpeech" 
              :loading="isGenerating"
              style="width: 100%">
              {{ isGenerating ? `生成中...(${processingProgress})` : '生成语音' }}
            </el-button>
          </el-form-item>
        </div>
      </el-form>

      <!-- 进度显示 -->
      <div v-if="textSegments.length > 0 && isGenerating" class="progress-info">
        <el-progress :percentage="progressPercentage" :format="progressFormat"></el-progress>
        <div class="segment-info">
          <span>当前处理: {{ currentSegmentIndex + 1 }}/{{ textSegments.length }}</span>
          <span>当前文本: {{ textSegments[currentSegmentIndex] }}</span>
        </div>
      </div>

      <!-- 播放信息 -->
      <div v-if="enableSegmentation && audioSegments.length > 0" class="playback-info">
        <div class="segment-status">
          <span>播放状态: {{ isPlaying ? '播放中' : '已暂停' }}</span>
          <span>当前播放: {{ currentPlayingIndex + 1 }}/{{ totalSegments }}</span>
        </div>
      </div>

      <!-- 音频播放器容器 - 隐藏真实播放器 -->
      <div class="audio-player-container" ref="audioContainer">
        <!-- 这个audio元素会被动态创建和移除 -->
      </div>
    </el-card>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'CosyVoice',
  data() {
    return {
      form: {},
      modes: ['预训练音色', '3s极速复刻', '跨语种复刻', '自然语言控制'],
      selectedMode: '预训练音色',
      speakers: [],
      selectedSpeaker: '',
      ttsText: '',
      instructText: '',
      promptText: '',
      speed: 1.0,
      audioFile: null,
      audioUrl: null,
      isGenerating: false,
      apiBaseUrl: 'http://localhost:8000',
      audioLoaded: false,
      audioDuration: 0,
      // 分段处理相关数据
      enableSegmentation: true,
      textSegments: [],
      audioSegments: [],
      currentSegmentIndex: 0,
      processingQueue: [],
      processingProgress: '',
      isProcessingSegments: false,
      // 播放控制
      currentPlayingIndex: 0,
      totalSegments: 0,
      isPlaying: false,
      audioElement: null,
      // 新增：是否有段落完成处理的标志
      allSegmentsProcessed: false,
      // SpringBoot交互相关
      springbootParam: '',
      receivedText: '',
      isLoadingText: false,
      springbootUrl: 'http://localhost:8080/test'
    }
  },
  computed: {
    progressPercentage() {
      if (this.textSegments.length === 0) return 0;
      return Math.round((this.currentSegmentIndex / this.textSegments.length) * 100);
    }
  },
  methods: {
    // 从SpringBoot获取文本
    async fetchTextFromSpringBoot() {
      if (!this.springbootParam.trim()) {
        this.$message.warning('请输入参数值');
        return;
      }
      
      this.isLoadingText = true;
      try {
        // 清理之前的语音资源
        this.releaseAllResources();
        this.receivedText = '';
        
        // 发送请求到SpringBoot
        const response = await axios.post(this.springbootUrl, { param: this.springbootParam }, {
          headers: {
            'Content-Type': 'application/json'
          }
        });
        
        // 检查响应是否有效
        if (!response.data) {
          throw new Error('后端返回无效数据');
        }
        
        // 获取返回的文本
        let textResult = '';
        
        // 检查返回数据格式，可能是直接字符串，也可能是对象
        if (typeof response.data === 'string') {
          textResult = response.data;
        } else if (response.data.text) {
          textResult = response.data.text;
        } else if (response.data.result) {
          textResult = response.data.result;
        } else if (response.data.data) {
          textResult = response.data.data;
        } else {
          textResult = JSON.stringify(response.data);
        }
        
        // 显示接收到的文本
        this.receivedText = textResult;
        
        // 设置为TTS文本
        this.ttsText = textResult;
        
        // 自动开始生成语音
        if (this.ttsText) {
          // 延迟一点，给用户看到接收到的文本
          setTimeout(() => {
            this.generateSpeech();
          }, 300);
        }
        
        this.$message.success('文本获取成功');
      } catch (error) {
        console.error('从SpringBoot获取文本失败:', error);
        this.$message.error('获取文本失败: ' + (error.message || '未知错误'));
      } finally {
        this.isLoadingText = false;
      }
    },
    
    async fetchSpeakers() {
      try {
        const response = await axios.get(`${this.apiBaseUrl}/speakers`);
        this.speakers = response.data.speakers;
        if (this.speakers.length > 0) {
          this.selectedSpeaker = this.speakers[0];
        }
      } catch (error) {
        this.$message.error('获取音色列表失败');
        console.error('获取音色列表失败:', error);
      }
    },
    handleModeChange() {
      this.cleanupAudio();
      this.audioFile = null;
      this.promptText = '';
      this.instructText = '';
      this.resetSegmentation();
    },
    handleAudioUpload(file) {
      this.audioFile = file.raw;
    },
    
    // 分段文本
    segmentText(text) {
      // 按标点符号分段
      const delimiters = ['，', '。', '!', '！', '?', '？', '；', '：', '\n'];
      let segments = [text];
      
      // 按每个分隔符分割
      for (const delimiter of delimiters) {
        let newSegments = [];
        for (const segment of segments) {
          // 跳过空段
          if (!segment.trim()) continue;
          
          // 按当前分隔符分割
          const parts = segment.split(delimiter);
          for (let i = 0; i < parts.length - 1; i++) {
            newSegments.push(parts[i] + delimiter);
          }
          
          // 最后一部分可能没有分隔符
          if (parts[parts.length - 1].trim()) {
            newSegments.push(parts[parts.length - 1]);
          }
        }
        segments = newSegments;
      }
      
      // 过滤掉空段并合并过短的段落
      const filteredSegments = segments.filter(seg => seg.trim().length > 0);
      const finalSegments = [];
      let currentSegment = '';
      
      for (const segment of filteredSegments) {
        if (currentSegment.length + segment.length < 20) {  // 合并较短的段落
          currentSegment += segment;
        } else {
          if (currentSegment) finalSegments.push(currentSegment);
          currentSegment = segment;
        }
      }
      
      if (currentSegment) finalSegments.push(currentSegment);
      
      return finalSegments;
    },
    
    // 重置分段处理状态
    resetSegmentation() {
      this.cleanupAudio();
      this.textSegments = [];
      this.audioSegments = [];
      this.currentSegmentIndex = 0;
      this.processingQueue = [];
      this.processingProgress = '';
      this.isProcessingSegments = false;
      this.currentPlayingIndex = 0;
      this.totalSegments = 0;
      this.isPlaying = false;
      this.allSegmentsProcessed = false; // 重置段落处理完成标志
    },
    
    // 清理音频资源
    cleanupAudio() {
      // 停止当前播放
      if (this.audioElement) {
        try {
          this.audioElement.pause();
          this.audioElement.onended = null;
          this.audioElement.onloadedmetadata = null;
          this.audioElement.onerror = null;
          this.audioElement.remove();
        } catch (e) {
          console.error('清理音频元素错误:', e);
        }
        this.audioElement = null;
      }
      
      // 清空容器
      const container = this.$refs.audioContainer;
      if (container) {
        container.innerHTML = '';
      }
      
      // 注意：我们不在这里释放URL，因为正在播放的段落可能需要URL
    },
    
    // 新方法：完全释放所有资源
    releaseAllResources() {
      // 释放所有音频URL
      for (const segment of this.audioSegments) {
        if (segment.url) {
          try {
            URL.revokeObjectURL(segment.url);
          } catch (e) {
            console.error('释放URL错误:', e);
          }
          segment.url = null;
        }
      }
      
      // 其他清理
      this.cleanupAudio();
    },
    
    // 处理下一段
    async processNextSegment() {
      if (this.currentSegmentIndex >= this.textSegments.length) {
        this.isProcessingSegments = false;
        this.isGenerating = false;
        this.totalSegments = this.audioSegments.length;
        this.allSegmentsProcessed = true; // 标记所有段落已处理完成
        console.log('全部文本处理完成，共生成', this.totalSegments, '段音频');
        return;
      }
      
      const segmentText = this.textSegments[this.currentSegmentIndex];
      this.processingProgress = `段落 ${this.currentSegmentIndex + 1}/${this.textSegments.length}`;
      
      try {
        let response;
        const formData = new FormData();
        const randomSeed = Math.floor(Math.random() * 1000000);

        if (['3s极速复刻', '跨语种复刻'].includes(this.selectedMode)) {
          if (!this.audioFile) {
            this.$message.warning('请上传示例音频');
            this.isProcessingSegments = false;
            this.isGenerating = false;
            return;
          }
          if (this.selectedMode === '3s极速复刻' && !this.promptText) {
            this.$message.warning('请输入示例文本');
            this.isProcessingSegments = false;
            this.isGenerating = false;
            return;
          }

          formData.append('tts_text', segmentText);
          formData.append('mode', this.selectedMode);
          formData.append('prompt_audio', this.audioFile);
          formData.append('speed', this.speed);
          formData.append('seed', randomSeed);
          if (this.promptText) {
            formData.append('prompt_text', this.promptText);
          }

          response = await axios.post(`${this.apiBaseUrl}/tts_clone`, formData, {
            responseType: 'blob'
          });
        } else {
          const requestData = {
            tts_text: segmentText,
            mode: this.selectedMode,
            speed: this.speed,
            seed: randomSeed
          };

          if (this.selectedMode === '预训练音色') {
            requestData.sft_speaker = this.selectedSpeaker;
          } else if (this.selectedMode === '自然语言控制') {
            if (!this.instructText) {
              this.$message.warning('请输入指令文本');
              this.isProcessingSegments = false;
              this.isGenerating = false;
              return;
            }
            requestData.instruct_text = this.instructText;
          }

          response = await axios.post(`${this.apiBaseUrl}/tts`, requestData, {
            responseType: 'blob'
          });
        }
        
        // 直接保存 Blob 数据，而不是 URL
        const blob = new Blob([response.data], { type: 'audio/wav' });
        console.log(`段落 ${this.currentSegmentIndex + 1} 音频数据大小:`, blob.size, '字节');
        
        // 保存当前段落的音频blob
        this.audioSegments.push({
          text: segmentText,
          blob: blob,  // 存储blob而不是url
          index: this.currentSegmentIndex,
          played: false
        });
        
        console.log(`段落 ${this.currentSegmentIndex + 1} 添加到队列，当前队列长度:`, this.audioSegments.length);
        
        // 如果是第一段且尚未开始播放，开始播放
        if (this.currentSegmentIndex === 0 && !this.isPlaying) {
          this.playSegment(0);
        }
        
        // 处理下一段
        this.currentSegmentIndex++;
        this.processNextSegment();
        
      } catch (error) {
        console.error(`段落 ${this.currentSegmentIndex + 1} 生成失败:`, error);
        this.$message.error(`段落 ${this.currentSegmentIndex + 1} 生成失败，跳过此段`);
        // 跳过失败的段落
        this.currentSegmentIndex++;
        this.processNextSegment();
      }
    },
    
    // 播放指定段落
    playSegment(index) {
      // 检查是否超出当前已有的段落
      if (index >= this.audioSegments.length) {
        // 如果正在处理中，且有后续段落即将生成，等待一段时间后再尝试
        if (this.isProcessingSegments && !this.allSegmentsProcessed) {
          console.log(`等待段落 ${index + 1} 处理完成...`);
          setTimeout(() => {
            this.playSegment(index);
          }, 500); // 等待500ms后再次尝试
          return;
        }
        
        // 已经处理完所有段落，播放结束
        console.log('播放完成：没有更多段落');
        this.isPlaying = false;
        this.$message.success('全部语音播放完成');
        return;
      }
      
      const segment = this.audioSegments[index];
      if (!segment || !segment.blob) {
        console.error('段落数据无效:', index);
        return;
      }
      
      console.log(`开始播放段落 ${index + 1}:`, segment.text);
      this.currentPlayingIndex = index;
      this.isPlaying = true;
      
      // 创建新的音频元素
      this.cleanupAudio();
      const audioContainer = this.$refs.audioContainer;
      const audio = document.createElement('audio');
      
      // 使用新的方式处理Blob数据
      try {
        // 确保blob有效
        if (segment.blob.size > 0) {
          // 创建媒体源
          const objectUrl = URL.createObjectURL(segment.blob);
          console.log(`为段落 ${index + 1} 创建URL:`, objectUrl);
          
          audio.src = objectUrl;
          audio.controls = true; // 添加控制条以便调试
          audio.style.width = '100%';
          
          // 存储URL以便清理
          segment.url = objectUrl;
          
          // 标记为已播放
          segment.played = true;
          
          // 设置事件处理
          audio.onloadedmetadata = () => {
            console.log(`段落 ${index + 1} 音频加载完成，时长:`, audio.duration.toFixed(1));
            audio.play().catch(error => {
              console.error('播放失败:', error);
              this.$message.warning('自动播放失败，请允许网站自动播放音频');
            });
          };
          
          audio.onended = () => {
            console.log(`段落 ${index + 1} 播放完成，准备播放下一段`);
            // 在下一帧执行，确保事件处理完成
            setTimeout(() => {
              this.playSegment(index + 1);
            }, 100);
          };
          
          audio.onerror = (e) => {
            console.error(`段落 ${index + 1} 播放错误:`, e);
            this.$message.error(`段落 ${index + 1} 播放失败, 尝试下一段`);
            // 尝试播放下一段
            setTimeout(() => {
              this.playSegment(index + 1);
            }, 500);
          };
          
          // 添加到容器
          audioContainer.appendChild(audio);
          this.audioElement = audio;
        } else {
          console.error(`段落 ${index + 1} blob数据大小为0`);
          this.$message.error(`段落 ${index + 1} 数据无效，跳过`);
          this.playSegment(index + 1);
        }
      } catch (error) {
        console.error(`处理段落 ${index + 1} 出错:`, error);
        this.$message.error(`段落 ${index + 1} 处理失败，跳过`);
        this.playSegment(index + 1);
      }
    },
    
    // 进度显示格式化
    progressFormat(percentage) {
      return `${percentage}%`;
    },
    
    async generateSpeech() {
      if (!this.ttsText) {
        this.$message.warning('请输入要转换的文本');
        return;
      }

      this.isGenerating = true;
      this.audioLoaded = false;
      
      // 清除之前的资源
      this.releaseAllResources();
      
      // 检查是否启用分段处理
      if (this.enableSegmentation && this.ttsText.length > 10) {
        // 分段处理
        this.resetSegmentation();
        this.allSegmentsProcessed = false; // 重置段落处理状态
        this.textSegments = this.segmentText(this.ttsText);
        console.log('文本已分为', this.textSegments.length, '段');
        
        if (this.textSegments.length === 0) {
          this.$message.warning('文本分段失败，请检查输入');
          this.isGenerating = false;
          return;
        }
        
        this.isProcessingSegments = true;
        this.currentSegmentIndex = 0;
        this.processNextSegment();
      } else {
        // 不分段处理
        try {
          let response;
          const formData = new FormData();
          // 生成随机种子
          const randomSeed = Math.floor(Math.random() * 1000000);

          if (['3s极速复刻', '跨语种复刻'].includes(this.selectedMode)) {
            if (!this.audioFile) {
              this.$message.warning('请上传示例音频');
              return;
            }
            if (this.selectedMode === '3s极速复刻' && !this.promptText) {
              this.$message.warning('请输入示例文本');
              return;
            }

            formData.append('tts_text', this.ttsText);
            formData.append('mode', this.selectedMode);
            formData.append('prompt_audio', this.audioFile);
            formData.append('speed', this.speed);
            formData.append('seed', randomSeed);
            if (this.promptText) {
              formData.append('prompt_text', this.promptText);
            }

            response = await axios.post(`${this.apiBaseUrl}/tts_clone`, formData, {
              responseType: 'blob'
            });
          } else {
            const requestData = {
              tts_text: this.ttsText,
              mode: this.selectedMode,
              speed: this.speed,
              seed: randomSeed
            };

            if (this.selectedMode === '预训练音色') {
              requestData.sft_speaker = this.selectedSpeaker;
            } else if (this.selectedMode === '自然语言控制') {
              if (!this.instructText) {
                this.$message.warning('请输入指令文本');
                return;
              }
              requestData.instruct_text = this.instructText;
            }

            response = await axios.post(`${this.apiBaseUrl}/tts`, requestData, {
              responseType: 'blob'
            });
          }

          const blob = new Blob([response.data], { type: 'audio/wav' });
          
          console.log('音频数据大小:', blob.size, '字节');
          if (blob.size < 1000) {
            this.$message.warning('生成的音频数据异常，可能不完整');
          }
          
          // 单段模式直接播放
          const audioContainer = this.$refs.audioContainer;
          const audio = document.createElement('audio');
          
          const objectUrl = URL.createObjectURL(blob);
          audio.src = objectUrl;
          audio.controls = true;
          audio.style.width = '100%';
          
          // 保存用于清理
          this.audioUrl = objectUrl;
          
          audio.onloadedmetadata = () => {
            console.log('单段音频加载完成，时长:', audio.duration.toFixed(1));
            audio.play().catch(error => {
              console.error('播放失败:', error);
              this.$message.warning('自动播放失败，请手动点击播放按钮');
            });
          };
          
          audio.onerror = (e) => {
            console.error('音频播放错误:', e);
            this.$message.error('音频加载失败，请重试');
          };
          
          // 清空容器并添加新元素
          audioContainer.innerHTML = '';
          audioContainer.appendChild(audio);
          this.audioElement = audio;
          
          this.$message.success('语音生成成功');
        } catch (error) {
          console.error('生成语音失败:', error);
          this.$message.error('生成语音失败，请重试');
        } finally {
          this.isGenerating = false;
        }
      }
    }
  },
  async created() {
    await this.fetchSpeakers();
  },
  beforeDestroy() {
    // 确保在组件销毁前清理资源
    this.releaseAllResources();
  }
}
</script>

<style scoped>
.cosy-container {
  max-width: 800px;
  margin: 20px auto;
  padding: 0 20px;
}

.main-card {
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}

.card-header {
  text-align: center;
}

.card-header h2 {
  margin: 0;
  color: #303133;
  font-size: 24px;
}

.cosy-form {
  margin-top: 20px;
}

.param-input {
  margin-bottom: 10px;
}

.received-text-box {
  padding: 10px;
  background-color: #f8f8f8;
  border-radius: 4px;
  border-left: 3px solid #409eff;
  max-height: 120px;
  overflow-y: auto;
}

.audio-player-container {
  margin-top: 20px;
}

.el-upload-dragger {
  width: 100%;
}

.el-upload {
  width: 100%;
}

.progress-info {
  margin: 20px 0;
  padding: 10px;
  background-color: #f8f8f8;
  border-radius: 4px;
}

.segment-info, .segment-status {
  margin-top: 10px;
  display: flex;
  justify-content: space-between;
  color: #606266;
  font-size: 14px;
}

.playback-info {
  margin: 20px 0;
  padding: 10px;
  background-color: #f0f9eb;
  border-radius: 4px;
  border-left: 3px solid #67c23a;
}

/* 响应式调整 */
@media screen and (max-width: 768px) {
  .cosy-container {
    padding: 0 10px;
  }
  
  .el-form-item {
    margin-bottom: 15px;
  }
}
</style>
